
#include <iostream>
#include <boost/asio.hpp>
#include <boost/spirit/core.hpp>
#include <boost/spirit/core.hpp>
#include <stack>
#include <string>
#include <sstream>

#include "WayMap.h"

////////////////////////////////////////////////////////////////////////////
//Commands
///////////////////////////////////////////////////////////////////////////
using namespace std;
using namespace boost::spirit;

enum ECmd
{
    EInitMap = 0
  , EClose
  , EAddCraters
  , EAddBoulders
  , EPathFrom
};

const char* strInit = "init";
const char* strClose = "close";
const char* strCraters = "craters";
const char* strBoulders = "boulders";
const char* strPathFrom = "path"; // x y  tox  toy
 
struct push_val
{
	push_val(stack<double>& eval_, int val)
    : eval(eval_),m_val(val) {}
	void operator()(char const*, char const*) const
    {
        eval.push(m_val);
    }
    stack<double>& eval;
	int m_val;
};

struct push_int
{
    push_int(stack<double>& eval_)
    : eval(eval_) {}

    void operator()(char const* str, char const* /*end*/) const
    {
        double n = strtod(str, 0);
        eval.push(n);
    }
    stack<double>& eval;
};

struct CmdParser : public grammar<CmdParser>
{
    CmdParser(stack<double>& eval_)
    : eval(eval_) {}

    template <typename ScannerT>
    struct definition
    {
        definition(CmdParser const& self)
        {
            integer = dtest[push_int(self.eval)];
			dtest = real_p;
            cmds = (str_p(strInit)>> space_p >> integer >> space_p>> integer)[push_val(self.eval,EInitMap)]
				   |(str_p(strClose))[push_val(self.eval,EClose)]
				   |(str_p(strCraters)>> +(space_p >> integer >> space_p>> integer >> space_p >>integer)) [push_val(self.eval,EAddCraters)]
				   |(str_p(strBoulders)>> +(space_p >> integer >> space_p>> integer >> space_p >>integer)) [push_val(self.eval,EAddBoulders)]
				   |(str_p(strPathFrom)>> space_p >> integer >> space_p >> integer >> space_p >> integer >> space_p >> integer)[push_val(self.eval,EPathFrom)];
		}

        rule<ScannerT> cmds, integer,dtest;
        rule<ScannerT> const&
        start() const { return cmds; }
    };

    stack<double>& eval;
};

//// circle Brezenhaim
void drawcircle(int xc, int yc, int r)
{
    int x;
    int y;
    int d;

    x = 0;
    y = r;
    d = 3-2*r;
    while(y>=x)
    {
        WouwSoft::SetBlockUW(x+xc, y+yc,0);
        WouwSoft::SetBlockUW(x+xc, -y+yc,0);
        WouwSoft::SetBlockUW(-x+xc, y+yc,0);
        WouwSoft::SetBlockUW(-x+xc, -y+yc,0);
        WouwSoft::SetBlockUW(y+xc, x+yc,0);
        WouwSoft::SetBlockUW(y+xc, -x+yc,0);
        WouwSoft::SetBlockUW(-y+xc, x+yc,0);
        WouwSoft::SetBlockUW(-y+xc, -x+yc,0);
        if( d<0 )
        {
            d = d+4*x+6;
        }
        else
        {
            d = d+4*(x-y)+10;
            y = y-1;
        }
        x = x+1;
    }
}


//////////////////////////////////////////////////////////////////////////////////////////
class CmdExecuter
{
public:
	CmdExecuter(): m_mapXSize(0), m_mapYSize(0) {};
	std::string ExecCmd(const std::string& str)
	{
		std::string resp("");
		while (!m_stack.empty()) m_stack.pop();
		CmdParser cmd_parser(m_stack);
		parse_info<> inf = boost::spirit::parse(str.c_str(),cmd_parser);

		ECmd cmd = (ECmd) (int)m_stack.top();
		m_stack.pop();
		switch(cmd)
		{
			case EInitMap: { double y = m_stack.top(); m_stack.pop(); double x = m_stack.top(); m_stack.pop();ExecInit(x,y);}break;
			case EClose: { ExecClose();}break;
			case EAddCraters: { ExecCraters();}break;
			case EAddBoulders: { ExecBoulders();}break;
			case EPathFrom: { ExecPath(resp);}break;
		};
		return resp;
	}
protected:
	void ExecInit( double mapX, double mapY )
	{
		m_mapXSize = (int)mapX, 
		m_mapYSize = (int)mapY, 
		WouwSoft::WayMapInit(100,100,0, mapX, 0, mapY);
		WouwSoft::SetBlock(50,50,100,7);
		std::cout<<"inited map" << mapX << mapY;
	}
	void ExecPath(std::string& response)
	{
		   double toy = m_stack.top();m_stack.pop();
		   double tox = m_stack.top();m_stack.pop();
		   double y = m_stack.top();m_stack.pop();
		   double x = m_stack.top();m_stack.pop();
		   WouwSoft::TWayShort res;
	       WouwSoft::GetWay( x, y, tox, toy, 2, res,3,1);
		   
		   std::ostringstream strm;
		   strm<<(int)res.Points;
		   for (int i = 0; i <  res.Points; i++)
		   {
			   strm<<" "<<res.Point[i].x <<" "<< res.Point[i].y;
		   }
		   response = strm.str();
	}
	void ExecCraters ()
	{
		while (!m_stack.empty())
		{
           double r = m_stack.top();m_stack.pop();
		   double y = m_stack.top();m_stack.pop();
		   double x = m_stack.top();m_stack.pop();
		   drawcircle((int)x,(int)y,(int)r);
		   std::cout<<"crater" << x << y << r;  
		}
	}
	void ExecBoulders ()
	{
		while(!m_stack.empty())
		{
           double r = m_stack.top();m_stack.pop();
		   double y = m_stack.top();m_stack.pop();
		   double x = m_stack.top();m_stack.pop();
		   drawcircle((int)x,(int)y,(int)r);
		   std::cout<<"boulder" << x << y << r;
		}
	}
	void ExecClose ()
	{
		WouwSoft::WayMapDestroy();
		exit(0);
	}
    int m_mapXSize;
	int m_mapYSize;
    stack<double> m_stack; 
};

//////////////////////////////////////////////////////////////////////////////////////////
// Server
/////////////////////////////////////////////////////////////////////////////////////////
using boost::asio::ip::tcp;
int main(int argc, char* argv[])
{
	

  if (argc < 2)
  {
	  std::cout<<"specify port number to listen";
	  exit(0);
  }

  try
  {
	boost::system::error_code error;
    boost::asio::io_service io_service;
	int nport = strtol(argv[1],0,10);
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), nport));
    tcp::socket socket(io_service);
    acceptor.accept(socket);
      
	for (;;)
    {  
	   boost::array<char,10000> buf;
       boost::system::error_code error;

       size_t len = socket.read_some(boost::asio::buffer(buf), error);

      if (error == boost::asio::error::eof)
        break;
      else if (error)
        throw boost::system::system_error(error);

	  std::string str(buf.data(),len);
	  CmdExecuter ce;
	  std::string response = ce.ExecCmd(str);
	  if( response.size() )
	  {
		  boost::system::error_code ignored_error;
          boost::asio::write(socket, boost::asio::buffer(response),
          boost::asio::transfer_all(), ignored_error);
	  }
    }
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

